﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xant.FlowEngine.Core;
using Xant.FlowEngine.Factory;
using Xant.FlowEngine.Interface;
using Dapper;
using System.Data;

namespace Xant.FlowEngine.Serialize
{
    public abstract class AbstractSerializer : ISerializer
    {

        public virtual T Load<T>(IDbConnection conn, int id, ref bool handled) where T : AbstractFlowObject
        {
            if (conn == null)
            {
                throw new ArgumentNullException();
            }
            return default(T);
        }

        public virtual void Insert<T>(IDbTransaction trans, T item, ref bool handled) where T : AbstractFlowObject
        {
            if (trans == null)
            {
                throw new ArgumentNullException();
            }
            if (item.RowVersion != 0)
            {
                throw new ApplicationException("该流程对象的[RowVersion]字段值已改变，请检查是否多次调用了AbstractSerializer.Insert方法。");
            }
            string sql;
            Type type = item.GetType();
            //如果该流程对象为ProcessInstance派生类，则在数据库的xfProcessInstance表中插入一行记录
            if (type.IsSubclassOf(typeof(ProcessInstance)) || type == typeof(ProcessInstance))//流程实例
            {
                var process = item as ProcessInstance;
                //为防止数据不一致，在新增记录到数据库时，将RowVersion字段固定赋值为1，而不从对象中直接获取属性，在插入成功后再将对象RowVersion属性值设为1
                sql = "INSERT INTO xfProcessInstance(Id, TemplateId, OwnerProcessId, Name, Status, RowVersion) VALUES(@id, @TemplateId, @OwnerProcessId, @Name, @Status, 1)";
                var count = trans.Connection.Execute(sql, process, trans);
                if (count == 0)
                {
                    throw new ApplicationException("乐观锁");
                }
                process.RowVersion = 1;
            }
            //如果该流程对象为AbstractActivity派生类，则在数据库的xfActivity表中插入一行记录
            else if(type.IsSubclassOf(typeof(AbstractActivity)))
            {
                var activity = item as AbstractActivity;
                //为防止数据不一致，在新增记录到数据库时，将RowVersion字段固定赋值为1，而不从对象中直接获取属性，在插入成功后再将对象RowVersion属性值设为1
                sql = "INSERT INTO xfActivity(Id, Type, ProcessId, Code, Name, Status, RowVersion) VALUES(@Id, @Type, @ProcessId, @Code, @Name, @Status, 1)";
                var count = trans.Connection.Execute(sql, activity, trans);
                if (count == 0)
                {
                    throw new ApplicationException("乐观锁");
                }
                activity.RowVersion = 1;
            }
        }

        public virtual void Update<T>(IDbTransaction trans, T item, ref bool handled) where T : AbstractFlowObject
        {
            if (trans == null)
            {
                throw new ArgumentNullException();
            }
            string sql;
            Type type = item.GetType();
            //如果该流程对象为ProcessInstance派生类，则更新xfProcessInstance表
            if (type.IsSubclassOf(typeof(ProcessInstance)) || type == typeof(ProcessInstance))//流程实例
            {
                var process = item as ProcessInstance;
                //为防止数据不一致，在更新记录到数据库时，先在SQL脚本中判断RowVersion字段值是否与内存中的值一致，并将RowVersion字段值增1，更新成功后再将对象RowVersion属性值增1
                sql = "UPDATE xfProcessInstance SET Name=@Name, Status=@Status, StartTime=@StartTime, EndTime=@EndTime, Invalid=@Invalid, RowVersion=RowVersion+1 WHERE Id=@Id AND RowVersion=@RowVersion";
                trans.Connection.Execute(sql, process, trans);
                process.RowVersion++;
            }
            //如果该流程对象为AbstractActivity派生类，则更新xfActivity表
            else if(type.IsSubclassOf(typeof(AbstractActivity)))
            {
                var activity = item as AbstractActivity;
                //为防止数据不一致，在更新记录到数据库时，先在SQL脚本中判断RowVersion字段值是否与内存中的值一致，并将RowVersion字段值增1，更新成功后再将对象RowVersion属性值增1
                sql = "UPDATE xfActivity SET Code=@Code, Name=@Name, Status=@Status, EntryTime=@EntryTime, LeaveTime=@LeaveTime, Invalid=@Invalid, RowVersion=RowVersion+1 WHERE Id=@Id AND RowVersion=@RowVersion";
                trans.Connection.Execute(sql, activity, trans);
                activity.RowVersion++;
            }
        }

        public virtual void Delete<T>(IDbTransaction trans, T item, ref bool handled) where T : AbstractFlowObject
        {
            if (trans == null)
            {
                throw new ArgumentNullException();
            }
        }

        public virtual void DeleteById<T>(IDbTransaction trans, int id, ref bool handled) where T : AbstractFlowObject
        {
            if (trans == null)
            {
                throw new ArgumentNullException();
            }
        }

    }

}
